qx.Class.define("lastfmbackup.model.RecentTracks", {
  
  extend : qx.core.Object,
  
  properties : {
    
    username : {
      check        : "String",
      deferredInit : true
    },
    
    blockSize : {
      check : "Number",
      init  : 128
    },
    
    retry : {
      check : "Boolean",
      init  : true
    }
    
  },
  
  events : {

    /**
     *  Fired when model's async method completes
     */
    load : "qx.event.type.Data",
    
    /**
     *  Fired when model's async method fails
     */
    fail : "qx.event.type.Data"
    
  },
  
  construct : function(username)
  {
    this.base(arguments);
    
    if(username)
    {
      this.initUsername(username);
    }
    
    this._apiKey = qx.core.Environment.get("lastfmbackup.apiKey");
  },
  
  members : {
    
    _apiKey  : null,
    _baseUrl : "http://ws.audioscrobbler.com/",
    _version : "2.0",
    _delays  : Array.range(1, 5).map(Math.exp).map(Math.round),
    
    
    _request : function(page)
    {
      var request = new qx.io.request.Jsonp("{0}{1}/".format(this._baseUrl, this._version));
      request.setRequestData({
        "api_key" : this._apiKey,
        "format"  : "json",
        "method"  : "user.getRecentTracks",
        "user"    : this.getUsername(),
        "limit"   : this.getBlockSize(),
        "page"    : page !== undefined ? page : 1
      });
      request.setTimeout(16000);
      request.setCache(false);
      request.setUserData("attempt", 0);
      request.setUserData("processed", false);
      request.addListener("fail", this._onFail, this);
      request.addListener("success", function()
      {
        qx.event.Timer.once(function()
        {
          if(request.getUserData("processed"))
          {
            request.dispose();
          }
        }, this, 0);
      }, this);
      
      return request;
    },
    
    _convert : function(track)
    {
      var nullEmpty = function(value)
      {
        return value !== "" ? value : null;
      };
      return {
        "date"  : Number(track["date"]["uts"]), 
        "track" : {
          "name" : nullEmpty(track["name"]),
          "mbid" : nullEmpty(track["mbid"])
        },
        "album" : {
          "name" : nullEmpty(track["album"]["#text"]),
          "mbid" : nullEmpty(track["album"]["mbid"])
        },
        "artist" : {
          "name" : nullEmpty(track["artist"]["#text"]),
          "mbid" : nullEmpty(track["artist"]["mbid"])
        }
      };
    },
    
    _onFail : function(event)
    {
      var target  = event.getTarget();
      var attempt = target.getUserData("attempt");
      if(this.getRetry() && attempt in this._delays)
      {
        target.debug("Request failed, next attempt #{0} in {1}s".format(attempt + 2, this._delays[attempt]));
        
        qx.event.Timer.once(target.send, target, this._delays[attempt] * 1000);
        target.setUserData("attempt", attempt + 1);
      }
      else
      {
        this.fireDataEvent("fail", {"type" : target.getUserData("type")});
      }
    },
    
    _onCountSuccess : function(event)
    {
      var request  = event.getTarget();
      var response = request.getResponse();
      if(!qx.lang.Type.isObject(response))
      {
        request.debug("Count request completed with malformed response '{0}'".format(response));
        this._onFail(event);
      }
      else if("error" in response)
      {
        request.debug("Count request completed with error '{0}'".format(response.message));
        this._onFail(event);
      }
      else
      {
        request.debug("Count request completed");
        
        var result = response["recenttracks"]["@attr"];
        ["page", "perPage", "totalPages", "total"].map(function(key)
        {
          result[key] = Number(result[key]);  
        });
        this.fireDataEvent("load", {
          "type" : request.getUserData("type"),
          "data" : result
        });
        
        request.setUserData("processed", true);
      }
    },
    
    count : function()
    {
      var request = this._request(1);
      request.setUserData("type", "count");
      request.addListener("success", this._onCountSuccess, this);
      request.send();
      request.debug("Requesting count");
    },
    
    _onPageSuccess : function(event)
    {
      var request  = event.getTarget();
      var response = request.getResponse();
      if(!qx.lang.Type.isObject(response))
      {
        request.debug("Page request completed with malformed response '{0}'".format(response));
        this._onFail(event);
      }
      else if("error" in response)
      {
        request.debug("Page request completed with error '{0}'".format(response.message));
        this._onFail(event);
      }
      else
      {
        request.debug("Page request completed");

        var data = null;
        try
        {
          // make sure 'listening now' is skipped
          var skip = 0;
          try
          {
            if(response["recenttracks"]['@attr']['page'] == '1')
            {
              if(response["recenttracks"]["track"][0]['@attr']['nowplaying'] == 'true')
              {
                skip = 1;
              }
            }
          }
          catch(ex) {}
          
          data = response["recenttracks"]["track"].slice(skip).map(this._convert); 
        }
        catch(ex)
        {
          request.debug("Page request completed, but failed to convert '{0}'".format(ex.message));
          this._onFail(event);
        }
        
        this.fireDataEvent("load", {
          "type" : request.getUserData("type"),
          "data" : data
        });
        
        request.setUserData("processed", true);        
      }
    },
    
    page : function(page)
    {
      var request = this._request(page);
      request.setUserData("type", "page");
      request.addListener("success", this._onPageSuccess, this);
      request.send();
      request.debug("Requesting page #{0}".format(page));
    }
    
  }
  
});